package org.abettor.octopus.ui;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Iterator;
import java.util.List;

import org.abettor.octopus.ui.proxy.MainDialogProxy;
import org.abettor.octopus.util.DriverFinder;
import org.abettor.octopus.util.PropertiesReader;
import org.abettor.octopus.worker.Dispatcher;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.TableEditor;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.events.MouseAdapter;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.ShellAdapter;
import org.eclipse.swt.events.ShellEvent;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.FileDialog;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.MessageBox;
import org.eclipse.swt.widgets.Monitor;
import org.eclipse.swt.widgets.ProgressBar;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Spinner;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableColumn;
import org.eclipse.swt.widgets.TableItem;
import org.eclipse.swt.widgets.Text;

/**
 * 程序主窗口类
 * @author abettor
 *
 */
public class MainDialog
{
	private final static String TITLE = "Octopus Universal Database Cracker";
	private final static String ICON = "./img/logo.png";
	private final static String BANNER = "./img/banner.png";
	private final static int WIDTH = 500;
	private final static int WIDTH_NEW = 700;
	private final static int HEIGHT = 390;

	private MainDialogProxy proxy = null;
	private Dispatcher dispatcher = null;

	private boolean canQuit = true;
	private boolean isPaused = false;
	private Display display;
	private Shell shell;
	private Combo connector;
	private Text url;
	private Text user;
	private Button userSel;
	private Text pass;
	private Button passSel;
	private Spinner thread;
	private Button reversedMode;
	private Button startButton;
	private Button pauseButton;
	private Button stopButton;
	private Table resultTable;
	private ProgressBar progress;

	/**
	 * 默认构造函数，创建一个主程序窗口对象
	 */
	public MainDialog()
	{
		display = Display.getDefault();
		proxy = new MainDialogProxy(this);
	}

	/**
	 * 显示主窗口对话框
	 *
	 */
	public void show()
	{
		Monitor monitor = display.getPrimaryMonitor();
		Rectangle rect = monitor.getBounds();
		Font fontLabel = new Font(display, "Sans", 10, SWT.NORMAL);
		Font fontButton = new Font(display, "Sans", 10, SWT.NORMAL);
		Image banner = new Image(display, BANNER);

		//	创建对话框Shell对象
		shell = new Shell(display, SWT.DIALOG_TRIM | SWT.MIN | SWT.SYSTEM_MODAL);
		shell.setText(TITLE);
		shell.setSize(WIDTH, HEIGHT);
		shell.setLocation((rect.width - WIDTH) / 2, (rect.height - HEIGHT) / 2);
		shell.addShellListener(new ShellAdapter()
		{
			public void shellClosed(ShellEvent e)
			{
				if(! canQuit)
				{
					e.doit = false;
					MessageBox msg = new MessageBox(shell, SWT.OK);
					msg.setText("Error ...");
					msg.setMessage("You cannot close this application when the crack threads is running.");
					msg.open();
				}
			}
		});

		//	设置图标
		Image logo = new Image(display, ICON);
		shell.setImage(logo);
		logo.dispose();

		//	开始在对话框上放置控件
		//	设置banner图片
		Label bannerLabel = new Label(shell, SWT.SHADOW_NONE);
		Rectangle imgRect = banner.getBounds();
		bannerLabel.setSize(imgRect.width, imgRect.height);
		bannerLabel.setImage(banner);
		bannerLabel.setLocation(6, 10);

		Label label = new Label(shell, SWT.SHADOW_NONE | SWT.LEFT);
		label.setText("Database:");
		label.setLocation(10, 173);
		label.setSize(65, 25);
		label.setFont(fontLabel);

		//	数据库名称下来列表
		connector = new Combo(shell, SWT.DROP_DOWN | SWT.READ_ONLY);
		connector.setLocation(80, 170);
		connector.setSize(100, 25);
		connector.addSelectionListener(new SelectionAdapter()
		{
			public void widgetSelected(SelectionEvent e)
			{
				String urlString = DriverFinder.getConnectionString(connector.getText());
				url.setText(urlString);
			}
		});

		//	设置下拉框的内容
		List<String> connNames = DriverFinder.getConnectors();
		Iterator it = connNames.iterator();
		while(it.hasNext())
		{
			String theConnName = (String) it.next();
			connector.add(theConnName);
		}

		//	读取默认数据库类型信息
		String connName = PropertiesReader.get("Connector");
		String urlString = PropertiesReader.get("URL");

		//	设置下拉框的默认内容
		int index = connNames.indexOf(connName);
		if(index >= 0)
		{
			connector.select(index);
		}
		else
		{
			connector.select(0);
		}

		//	数据库连接字符串
		url = new Text(shell, SWT.SHADOW_IN | SWT.LEFT | SWT.BORDER);
		url.setLocation(180, 170);
		url.setSize(300, 25);

		//	设置数据库连接字符串的默认值
		if(urlString != null && ! urlString.equals(""))
		{
			url.setText(urlString);
		}
		else
		{
			url.setText(DriverFinder.getConnectionString(connName));
		}

		label = new Label(shell, SWT.SHADOW_NONE | SWT.LEFT);
		label.setText("User name dictionary:");
		label.setLocation(10, 203);
		label.setSize(140, 25);
		label.setFont(fontLabel);

		//	用户名字典
		user = new Text(shell, SWT.SHADOW_IN | SWT.LEFT | SWT.BORDER);
		user.setLocation(155, 200);
		user.setSize(245, 25);
		user.setText(PropertiesReader.get("UserDic"));

		userSel = new Button(shell, SWT.CENTER);
		userSel.setLocation(400, 200);
		userSel.setSize(80, 25);
		userSel.setText("Browse ...");
		userSel.setFont(fontButton);
		userSel.addMouseListener(new MouseAdapter()
		{
			public void mouseDown(MouseEvent e)
			{
				String [] extFilters = new String[] {"*.dic"};
				FileDialog fileDlg = new FileDialog(shell, SWT.OPEN);
				fileDlg.setText("Open ...");
				fileDlg.setFilterExtensions(extFilters);
				fileDlg.setFilterPath("./dat");
				fileDlg.open();
				String fileName = fileDlg.getFileName();
				String filePath = fileDlg.getFilterPath();
				if(fileName != null && ! "".equals(fileName))
				{
					user.setText(filePath + File.separator + fileName);
				}
			}
		});

		label = new Label(shell, SWT.SHADOW_NONE | SWT.LEFT);
		label.setText("Password dictionary:");
		label.setLocation(10, 233);
		label.setSize(140, 25);
		label.setFont(fontLabel);

		//	密码字典
		pass = new Text(shell, SWT.SHADOW_IN | SWT.LEFT | SWT.BORDER);
		pass.setLocation(155, 230);
		pass.setSize(245, 25);
		pass.setText(PropertiesReader.get("PassDic"));

		passSel = new Button(shell, SWT.CENTER);
		passSel.setLocation(400, 230);
		passSel.setSize(80, 25);
		passSel.setText("Browse ...");
		passSel.setFont(fontButton);
		passSel.addMouseListener(new MouseAdapter()
		{
			public void mouseDown(MouseEvent e)
			{
				String [] extFilters = new String[] {"*.dic"};
				FileDialog fileDlg = new FileDialog(shell, SWT.OPEN);
				fileDlg.setText("Open ...");
				fileDlg.setFilterExtensions(extFilters);
				fileDlg.setFilterPath("./dat");
				fileDlg.open();
				String fileName = fileDlg.getFileName();
				String filePath = fileDlg.getFilterPath();
				if(fileName != null && ! "".equals(fileName))
				{
					pass.setText(filePath + File.separator + fileName);
				}
			}
		});

		label = new Label(shell, SWT.SHADOW_NONE | SWT.LEFT);
		label.setText("Thread number:");
		label.setLocation(10, 263);
		label.setSize(110, 25);
		label.setFont(fontLabel);

		//	线程数
		thread = new Spinner(shell, SWT.BORDER);
		thread.setLocation(120, 260);
		thread.setSize(100, 25);
		thread.setMinimum(1);
		thread.setMaximum(100);
		thread.setSelection(Integer.parseInt(PropertiesReader.get("ThreadNumber")));

		label = new Label(shell, SWT.SHADOW_NONE | SWT.LEFT);
		label.setText("Use reversed combination mode:");
		label.setLocation(240, 263);
		label.setSize(215, 25);
		label.setFont(fontLabel);

		//	使用reversed模式
		reversedMode = new Button(shell, SWT.CHECK | SWT.RIGHT);
		reversedMode.setLocation(455, 260);
		reversedMode.setSize(20, 25);
		reversedMode.setSelection(Boolean.parseBoolean(PropertiesReader.get("ReversedMode")));

		label = new Label(shell, SWT.SHADOW_NONE | SWT.LEFT);
		label.setText("Progress:");
		label.setLocation(10, 293);
		label.setSize(65, 25);
		label.setFont(fontLabel);

		progress = new ProgressBar(shell, SWT.HORIZONTAL | SWT.SMOOTH);
		progress.setLocation(80, 290);
		progress.setSize(400, 25);
		progress.setMinimum(0);
		progress.setMaximum(100);

		//	开始破解按钮
		startButton = new Button(shell, SWT.CENTER);
		startButton.setLocation(10, 320);
		startButton.setSize(80, 25);
		startButton.setText("Start");
		startButton.setFont(fontButton);
		startButton.addMouseListener(new MouseAdapter()
		{
			public void mouseDown(MouseEvent e)
			{
				resultTable.removeAll();
				progress.setSelection(0);
				setRunning();
			}
		});

		//	暂停破解按钮
		pauseButton = new Button(shell, SWT.CENTER);
		pauseButton.setLocation(90, 320);
		pauseButton.setSize(80, 25);
		pauseButton.setText("Pause");
		pauseButton.setFont(fontButton);
		pauseButton.setEnabled(false);
		pauseButton.addMouseListener(new MouseAdapter()
		{
			public void mouseDown(MouseEvent e)
			{
				setPaused();
			}
		});

		//	结束破解按钮
		stopButton = new Button(shell, SWT.CENTER);
		stopButton.setLocation(170, 320);
		stopButton.setSize(80, 25);
		stopButton.setText("Stop");
		stopButton.setFont(fontButton);
		stopButton.setEnabled(false);
		stopButton.addMouseListener(new MouseAdapter()
		{
			public void mouseDown(MouseEvent e)
			{
				setTerminated();
			}
		});

		//	关于按钮
		Button aboutButton = new Button(shell, SWT.CENTER);
		aboutButton.setLocation(360, 320);
		aboutButton.setSize(80, 25);
		aboutButton.setText("About");
		aboutButton.setFont(fontButton);
		aboutButton.addMouseListener(new MouseAdapter()
		{
			public void mouseDown(MouseEvent e)
			{
				AboutDialog about = new AboutDialog(shell);
				about.show();
			}
		});

		//	显示结果按钮
		Button resultButton = new Button(shell, SWT.CENTER | SWT.TOGGLE);
		resultButton.setLocation(440, 320);
		resultButton.setSize(40, 25);
		resultButton.setText(">>");
		resultButton.setFont(fontButton);
		resultButton.addMouseListener(new MouseAdapter()
		{
			public void mouseDown(MouseEvent e)
			{
				Button btn = (Button) e.widget;
				if(! btn.getSelection())
				{
					btn.setText("<<");
					shell.setSize(WIDTH_NEW, HEIGHT);
				}
				else
				{
					btn.setText(">>");
					shell.setSize(WIDTH, HEIGHT);
				}
			}
		});

		label = new Label(shell, SWT.SHADOW_NONE | SWT.LEFT);
		label.setText("Result:");
		label.setLocation(500, 13);
		label.setSize(180, 25);
		label.setFont(fontLabel);

		//	结果列表
		resultTable = new Table(shell, SWT.FULL_SELECTION | SWT.HIDE_SELECTION | SWT.BORDER);
		resultTable.setLocation(500, 40);
		resultTable.setSize(180, 270);
		resultTable.setHeaderVisible(true);

		TableColumn columnUser = new TableColumn(resultTable, SWT.CENTER);
		columnUser.setWidth(80);
		columnUser.setText("User name");
		columnUser.setResizable(true);
		TableColumn columnPass = new TableColumn(resultTable, SWT.LEFT);
		columnPass.setWidth(90);
		columnPass.setText("Password");
		columnPass.setResizable(true);
		final TableEditor editor = new TableEditor(resultTable);
		editor.grabHorizontal = true;
		editor.horizontalAlignment = SWT.LEFT;
		editor.minimumWidth = 50;
		resultTable.addSelectionListener(new SelectionAdapter()
		{
			public void widgetSelected(SelectionEvent e)
			{
				Control oldEditor = editor.getEditor();
				if (oldEditor != null)
				{
					oldEditor.dispose();
				}

				TableItem item = (TableItem) e.item;
				if(item == null)
				{
					return;
				}

				Text newEditor = new Text(resultTable, SWT.NONE);
				newEditor.setText(item.getText(1));
				newEditor.addModifyListener(new ModifyListener()
				{
					public void modifyText(ModifyEvent event)
					{
						Text txt = (Text) editor.getEditor();
						editor.getItem().setText(1, txt.getText());
					}
				});
				newEditor.selectAll();
				newEditor.setFocus();
				editor.setEditor(newEditor, item, 1);
			}
		});

		//	导出结果列表
		Button export = new Button(shell, SWT.CENTER);
		export.setLocation(600, 320);
		export.setSize(80, 25);
		export.setText("Export");
		export.setFont(fontButton);
		export.addMouseListener(new MouseAdapter()
		{
			public void mouseDown(MouseEvent e)
			{
				String [] extFilters = new String[] {"*.txt"};
				FileDialog fileDlg = new FileDialog(shell, SWT.SAVE);
				fileDlg.setText("Save ...");
				fileDlg.setFilterExtensions(extFilters);
				fileDlg.setFilterPath("./dat");
				String strDest = fileDlg.open();
				if(strDest != null && ! "".equals(strDest))
				{
					FileWriter writer = null;
					BufferedWriter bufWriter = null;
					try
					{
						File file = new File(strDest);
						if(file.exists())
						{
							MessageBox messageBox = new MessageBox(shell, SWT.YES | SWT.NO);
							messageBox.setMessage("The file \"" + fileDlg.getFileName() + "\" has existed.\nDo you want to overwrite it ?");
							messageBox.setText("Warning ...");
							int choose = messageBox.open();
							if(choose == SWT.NO)
							{
								return;
							}
						}

						writer = new FileWriter(file);
						bufWriter = new BufferedWriter(writer);
					}
					catch (IOException e1)
					{
						e1.printStackTrace();
						try
						{
							bufWriter.close();
							writer.close();
						}
						catch (IOException e2)
						{
							e2.printStackTrace();
						}
						return;
					}

					TableItem [] items = resultTable.getItems();
					for(int i = 0; i < items.length; i ++)
					{
						String user = items[i].getText(0);
						String pass = items[i].getText(1);
						try
						{
							bufWriter.write(user + "\t" + pass);
							bufWriter.newLine();
						}
						catch (IOException e1)
						{
							e1.printStackTrace();
							try
							{
								bufWriter.close();
								writer.close();
							}
							catch (IOException e2)
							{
								e2.printStackTrace();
							}
							return;
						}
					}

					try
					{
						bufWriter.flush();
						bufWriter.close();
						writer.close();
					}
					catch (IOException e1)
					{
						e1.printStackTrace();
						return;
					}
				}
			}
		});

		//	开始窗口主循环
		shell.open();
		while (! shell.isDisposed())
		{
			if (! display.readAndDispatch())
			{
				display.sleep();
			}
		}

		//	释放资源
		editor.dispose();
		resultTable.dispose();
		banner.dispose();
		fontButton.dispose();
		fontLabel.dispose();
		shell.dispose();
	}

	/**
	 * 可供外部访问的将界面设置为已经停止工作的状态
	 *
	 */
	public void setStopped()
	{
		connector.setEnabled(true);
		url.setEnabled(true);
		user.setEnabled(true);
		userSel.setEnabled(true);
		pass.setEnabled(true);
		passSel.setEnabled(true);
		thread.setEnabled(true);
		reversedMode.setEnabled(true);
		startButton.setEnabled(true);
		pauseButton.setText("Pause");
		pauseButton.setEnabled(false);
		stopButton.setEnabled(false);
		isPaused = false;
		canQuit = true;
	}

	/**
	 * 获取Display对象，用于修改窗口成员对象
	 */
	public Display getDisplay()
	{
		return display;
	}

	/**
	 * 获取用于连接数据库的标识名称
	 */
	public String getConnector()
	{
		return connector.getText();
	}

	/**
	 * 获取进度条对象
	 */
	public ProgressBar getProgress()
	{
		return progress;
	}

	/**
	 * 获取数据库连接字符串
	 */
	public String getUrl()
	{
		return url.getText();
	}

	/**
	 * 获取密码字典文件名称
	 */
	public String getPass()
	{
		return pass.getText();
	}

	/**
	 * <p>检查是否以逆向方式破解</p>
	 * <p>所谓逆向方式，是指逐密码（而不是通常的逐用户名）破解的方式</p>
	 */
	public boolean isReversedMode()
	{
		return reversedMode.getSelection();
	}

	/**
	 * 获取设定的破解线程数
	 */
	public int getThread()
	{
		return thread.getSelection();
	}

	/**
	 * 获取用户名字典文件名称
	 */
	public String getUser()
	{
		return user.getText();
	}

	/**
	 * 获取结果列表对象
	 */
	public Table getResultTable()
	{
		return resultTable;
	}

	/**
	 * 获取主窗口Shell对象
	 */
	public Shell getShell()
	{
		return shell;
	}

	//	设置为运行状态
	private void setRunning()
	{
		canQuit = false;
		isPaused = false;
		connector.setEnabled(false);
		url.setEnabled(false);
		user.setEnabled(false);
		userSel.setEnabled(false);
		pass.setEnabled(false);
		passSel.setEnabled(false);
		thread.setEnabled(false);
		reversedMode.setEnabled(false);
		startButton.setEnabled(false);
		pauseButton.setEnabled(true);
		stopButton.setEnabled(true);

		//	启动调度线程，并令其启动破解线程
		dispatcher = new Dispatcher(proxy);
		dispatcher.start();
	}

	//	设置为暂停状态
	private void setPaused()
	{
		if(isPaused)
		{
			//	通知调度线程重新启动破解线程
			dispatcher.thaw();

			thread.setEnabled(false);
			pauseButton.setText("Pause");
		}
		else
		{
			//	通知调度线程停止所有破解线程
			dispatcher.freeze();

			thread.setEnabled(true);
			pauseButton.setText("Resume");
		}

		isPaused = ! isPaused;
	}

	//	设置为手动停止状态
	private void setTerminated()
	{
		// 停止包括调度线程在内的所有线程
		dispatcher.end();
	}
}
